#include "SimpleRender.h"


SimpleRender* SimpleRender::pRender = NULL;

void SimpleRender::Init()
{
	 CreateD3DWindow();
	 Set3DProjection();

	 SetFont("Arial", 38);

	 scale2DX = 0.7f;
	 scale2DY = 0.7f;
}

void SimpleRender::ClearScreen()
{
	// default - clear screen with black
	ClearScreen(D3DXCOLOR(0,0,0,1));
}

void SimpleRender::ClearScreen(D3DXCOLOR backGroundColor)
{
	 //Clear the backbuffer to a blue color
	//g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );

	// Clear the backbuffer and the zbuffer
	g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
		backGroundColor, 1.0f, 0 );
}

void SimpleRender::CreateD3DWindow()
{
	// Register the window class
	WNDCLASSEX wc =
	{
		sizeof( WNDCLASSEX ), CS_CLASSDC, SimpleRender::MsgProc, 0L, 0L,
		GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
		L"D3D Tutorial", NULL
	};
	RegisterClassEx( &wc );

	hInst = wc.hInstance;

	// Create the application's window
	hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 02: Vertices", //title
		WS_OVERLAPPEDWINDOW, 100, 100, SCREENWIDTH, SCREENHEIGHT, // size and position
		NULL, NULL, hInst, NULL );

	// register a D3D device with this window
	if SUCCEEDED( InitD3D( hWnd ) )
	{
		// show the window
		ShowWindow( hWnd, SW_SHOWDEFAULT );
		UpdateWindow( hWnd );

		// success
		//return 1;
	}
	else
	{
	//	return; //failure :(
	}
}

void SimpleRender::Set3DProjection()
{
	float aspectRatio = ((float)SCREENWIDTH) / ((float)SCREENHEIGHT);
	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4, aspectRatio, 1.0f, 10000.0f );
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );




}

void SimpleRender::Set2DProjection()
{
	D3DVIEWPORT9 vp;
	g_pd3dDevice->GetViewport(&vp);

	/*IDirect3DSurface9*  bb;
	g_pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&bb);
	D3DSURFACE_DESC* backbuffer = new D3DSURFACE_DESC();
	bb->GetDesc(backbuffer);*/

	D3DXMatrixOrthoOffCenterRH(&matProj,0,static_cast<float>(vp.Width),static_cast<float>(vp.Height),0,0,1000);
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

}

// create the D3D object
HRESULT SimpleRender::InitD3D( HWND hWnd )
{
	// Create the D3D object.
	if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
		return E_FAIL;

	// Set up the structure used to create the D3DDevice
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory( &d3dpp, sizeof( d3dpp ) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_2_SAMPLES;
	
	// z buffer
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	// Create the D3DDevice
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_HARDWARE_VERTEXPROCESSING,
		&d3dpp, &g_pd3dDevice ) ) )
	{
		return E_FAIL;
	}

	// Device state would normally be set here
	// Turn off culling
	g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

	// Enable Depath buffer
	g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

	//Enable alpha blending
	g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);

	//g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
	// antialiasing
	g_pd3dDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, TRUE);
	g_pd3dDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE);	

	return S_OK;
}

SimpleRender::~SimpleRender(void)
{
	CleanupD3D();
	CleanupFonts();
}

// font rendering
void SimpleRender::DrawText( signed int x, signed int y, const char *Text, D3DCOLOR c )
{
	RECT r;
	D3DVIEWPORT9 vp;
	g_pd3dDevice->GetViewport(&vp);

	r.right = vp.Width; r.bottom = vp.Height;
	r.top = y; r.left = x;

	if(CurrentFont)
	{
		CurrentFont->Draw(Text,r,c);
	}
}

void SimpleRender::DrawText( signed int x, signed int y, const char *Text )
{
	DrawText(x,y,Text,Colour::WHITE);
}


//set current font
void SimpleRender::SetFont( const char *Name, unsigned int Size)
{
	FontList::const_iterator it = mFontList.begin();

	// iterator through our loaded font list
	while(it != mFontList.end())
	{
		// if name and size match
		if( _stricmp((*it)->Name,Name) == 0 && (*it)->Size == Size)
		{
			CurrentFont = (*it);
			return; // match found! return
		}

		it++;
	}

	// if no match found, create new font
	FontData *pFont = new FontData();

	// init new fontdata
	pFont->Init(Name,Size, FW_NORMAL);

	// load using D3D helper
	LPCWSTR pwstr = char2wstr(pFont->Name);

	D3DXCreateFont( g_pd3dDevice, pFont->Size, 0, pFont->Flags, 0, FALSE, DEFAULT_CHARSET,
		OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
		pwstr, &pFont->pFont);

	assert(pFont);

	delete[] pwstr;

	// if loading succeeded, add to font database
	if(pFont->pFont)
	{
		mFontList.push_back(pFont);
		CurrentFont = pFont;
	}
}

void SimpleRender::FontData::Init( const char* name, unsigned int size, unsigned int flags )
{
	Size = size;
	Name = new char[strlen(name)+1];
	memset(Name,0,strlen(name)+1);
	memcpy(Name,name,strlen(name)+1);
	Flags = flags;
}

void SimpleRender::FontData::Draw( const char* Text,const RECT& r,D3DCOLOR c ) const
{
	RECT r2 = r;

	LPCWSTR pwstr = char2wstr(Text);
	pFont->DrawText(NULL,pwstr,-1,&r2,DT_LEFT , D3DCOLOR_ARGB(255,0,0,0));

	r2.top = r.top-1; r2.left = r.left-1;
	pFont->DrawText(NULL,pwstr,-1,&r2,DT_LEFT , c);

	delete[] pwstr;
}

void SimpleRender::CleanupFonts()
{
	for(unsigned int i=0; i<mFontList.size(); i++)
		delete mFontList[i];
}

void SimpleRender::DrawQuad2D( signed int x, signed int y, signed int width, signed int height, D3DCOLOR c )
{
	VERTEXPOSCOL Verts[6];

	Verts[0].pos.x = static_cast<float>(x  ); 
	Verts[0].pos.y = static_cast<float>(y  ); 
	Verts[0].pos.z = 0;
	Verts[0].colour = c;

	Verts[1].pos.x = static_cast<float>(x+width); 
	Verts[1].pos.y = static_cast<float>(y  ); 
	Verts[1].pos.z = 0;
	Verts[1].colour = c;

	Verts[2].pos.x = static_cast<float>(x+width); 
	Verts[2].pos.y = static_cast<float>(y+height); 
	Verts[2].pos.z = 0;
	Verts[2].colour = c;

	Verts[3].pos.x = static_cast<float>(x  ); 
	Verts[3].pos.y = static_cast<float>(y  ); 
	Verts[3].pos.z = 0;
	Verts[3].colour = c;

	Verts[4].pos.x = static_cast<float>(x+width); 
	Verts[4].pos.y = static_cast<float>(y+height); 
	Verts[4].pos.z = 0;
	Verts[4].colour = c;

	Verts[5].pos.x = static_cast<float>(x  ); 
	Verts[5].pos.y = static_cast<float>(y+height); 
	Verts[5].pos.z = 0;
	Verts[5].colour = c;

	Render(Verts[0],2,D3DPT_TRIANGLELIST);
}

void SimpleRender::Render( const VERTEXPOSCOL& rVerts, unsigned int PrimCount,D3DPRIMITIVETYPE ePrimType )
{
	/*D3DXMATRIXA16 WorldScale;
	D3DXMatrixIdentity(&WorldScale);
	D3DXMatrixScaling(&WorldScale,scale2DX,scale2DY,1.0f);

	g_pd3dDevice->SetTransform( D3DTS_WORLD, &WorldScale );*/


	// Create the vertex buffer.
	g_pd3dDevice->SetFVF( VERTEXPOSCOL::GetFVF() );
	g_pd3dDevice->DrawPrimitiveUP(ePrimType,PrimCount,&rVerts,sizeof(VERTEXPOSCOL));

}

LPCWSTR SimpleRender::char2wstr( const char* in )
{

	WCHAR *out = new WCHAR[strlen(in)+1];

	const char *pStr = in;
	WCHAR *pOut = out;

	while(*pStr != NULL)
	{
		*pOut++ = *pStr++;
	}

	*pOut = NULL;

	return out;
}

void SimpleRender::FontData::Reset()
{
	pFont = NULL;
	Name = NULL;
}

SimpleRender::FontData::FontData()
{
	Reset();
}

SimpleRender::FontData::~FontData()
{
	if(Name)
	{
		delete Name;
	}

	if(pFont)
	{
		pFont->Release();
	}

	Reset();
}
